24暑假算法刷题 | 您所在的位置:网站首页 › 查找重复字符串 leetcode › 24暑假算法刷题 |
目录
151. 反转字符串中的单词题目描述题解
28. 找出字符串中第一个匹配项的下标题目描述题解
459. 重复的子字符串题目描述题解
卡码网 55. 右旋字符串题目描述题解
151. 反转字符串中的单词
点此跳转题目链接 题目描述给你一个字符串 s ,请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 **注意:**输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。 示例 1: 输入:s = "the sky is blue" 输出:"blue is sky the"示例 2: 输入:s = " hello world " 输出:"world hello" 解释:反转后的字符串中不能存在前导空格和尾随空格。示例 3: 输入:s = "a good example" 输出:"example good a" 解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。提示: 1 if (c != ' ') word += c; else { if (word != "") { wordStack.push(word); word = ""; } } } string res = ""; while (!empty(wordStack)) { res += wordStack.top(); res += " "; wordStack.pop(); } res.pop_back(); // 去掉结尾的空格 return res; }此外,如果考虑原地修改字符串,将空间复杂度降为 O ( 1 ) O(1) O(1) ,可以综合运用双指针等方法实现,总体思路为 去除字符串中多余的空格反转整个字符串反转字符串的每一个单词(就把单词本身又倒回来了)代码实现(C++)参考代码随想录: class Solution { public: void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 [] for (int i = start, j = end; i //去除所有空格并在相邻单词之间添加空格, 快慢指针。 int slow = 0; //整体思想参考https://programmercarl.com/0027.移除元素.html for (int i = 0; i //遇到非空格就处理,即删除所有空格。 if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。 while (i removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。 reverse(s, 0, s.size() - 1); int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。 for (int i = 0; i //到达空格或者串尾,说明一个单词结束。进行翻转。 reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。 start = i + 1; //更新下一个单词的开始下标start } } return s; } }; 28. 找出字符串中第一个匹配项的下标点此跳转题目链接 题目描述给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。 示例 1: 输入:haystack = "sadbutsad", needle = "sad" 输出:0 解释:"sad" 在下标 0 和 6 处匹配。 第一个匹配项的下标是 0 ,所以返回 0 。示例 2: 输入:haystack = "leetcode", needle = "leeto" 输出:-1 解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。提示: 1 while (i > 0 && needle[i] != needle[j]) // 不匹配:i回退 i = next[i - 1]; if (needle[i] == needle[j]) i++; next[j] = i; } return next; } int strStr(string haystack, string needle) { vector next = getNextArr(needle); for (int i = 0, j = 0; i // 如果可以由一个子串重复多次构成,则该子串肯定在开头就能找到 for (int len = 1; len string tempSub = s.substr(start, len); if (tempSub != sub) break; } if (start >= s.size()) return true; } return false; }这样时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,较高。 一种优雅的解法如下: bool repeatedSubstringPatternII(string s) { string ts = s + s; ts.erase(ts.begin()), ts.erase(ts.end() - 1); // 掐头去尾 return ts.find(s) != string::npos; }原理如下: 如果存在满足题目条件的子串 sub ,则原字符串 s 可以表示为 subsub...subsub(n个sub)那么将两个 s 首位相连,可以得到 ts : subsub...subsub(2n个sub) ,显然其中又产生了新的若干 s将 ts “掐头去尾”,排除原来的那两个 s :_ubsub...subsu_ ,如果中间仍能找到 s ,则 s 可以由重复子串 sub 构成上面代码中用到了 find() ,我们可以用自己的KMP算法替换,以进一步优化。不过这题更好的方案是利用KMP中的next数组实现: vector getNextArr(const string &needle) { vector next(needle.size(), 0); for (int i = 0, j = 1; j vector next = getNextArr(s); int n = s.size(); int len = next[n - 1]; return len > 0 && n % (n - len) == 0; }KMP算法及其next数组相关内容参见 LeetCode Q28-笔记) 下面推导一下这种解法的原理。 根据next数组的定义,如果 s 是由 n n n 个 sub 重复构成的,那么它一定有长度为 ( n − 1 ) L s u b (n - 1)L_{sub} (n−1)Lsub 的最长相等前后缀,而最长相等前后缀的长度就是next数组最后一个值(由next数组的定义可得)。同时 L s = n L s u b L_s = nL_{sub} Ls=nLsub 二者相减即得到重复子串的长度 L s u b L_{sub} Lsub ,这个值自然应当能被 L s L_s Ls 整除。 卡码网 55. 右旋字符串点此跳转题目链接 题目描述字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。 例如,对于输入字符串 “abcdefg” 和整数 2,函数应该将其转换为 “fgabcde”。 输入描述 输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。 输出描述 输出共一行,为进行了右旋转操作后的字符串。 输入示例 2 abcdefg输出示例 fgabcde提示信息 数据范围: 1 std::reverse(s.begin(), s.end()); std::reverse(s.begin(), s.begin() + k); std::reverse(s.begin() + k, s.end()); } 类似题目中,这种“先反转整体,再反转局部”的思想还蛮常见的。 |
CopyRight 2018-2019 实验室设备网 版权所有 |